#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <libxml/parser.h>
#include <libxml/xmlschemas.h>

#include <xmlsec/xmlsec.h>
#include <xmlsec/crypto.h>
#include <xmlsec/xmldsig.h>
#include <xmlsec/xmltree.h>
#include <xmlsec/list.h>

#include "common.h"

void printReferences(xmlSecDSigCtxPtr ds) {
  int i, fd;
  xmlSecDSigReferenceCtxPtr r;
  xmlSecBufferPtr buf;

  fd = open("/tmp/output-c-failed.xml", O_TRUNC|O_CREAT|O_WRONLY, 0600);
  assert(fd >= 3);
  for(i = 0; i < xmlSecPtrListGetSize(&ds->signedInfoReferences); i++) {
    r = xmlSecPtrListGetItem(&ds->signedInfoReferences, i);
    assert(r);
    buf = xmlSecDSigReferenceCtxGetPreDigestBuffer(r);
    assert(buf);
    fprintf(stderr, "signedinfo reference %d (%d):\n", i, buf->size);
    write(2, buf->data, buf->size);
    write(fd, buf->data, buf->size);
    fprintf(stderr, "\n end signedinfo reference\n");
  }
  close(fd);
}

void libSetup() {
  xmlInitParser();
  assert(!xmlSecInit());
#ifndef XMLSEC_NO_CRYPTO_DYNAMIC_LOADING
  assert(!xmlSecCryptoDLLoadLibrary("openssl"));
#endif
  assert(!xmlSecCryptoAppInit(NULL));
  assert(!xmlSecCryptoInit());
}

struct signedDocument *validateDocument(char *schema, xmlNodePtr root) {
  struct signedDocument *ret;
  xmlNodePtr keyinfo;
  xmlSecDSigReferenceCtxPtr ref;
  void *content;
  xmlSchemaParserCtxtPtr parser;
  xmlSchemaValidCtxtPtr vctx;
  xmlSchemaPtr schemap;
  xmlDocPtr signedDoc;
  int i;
  xmlSecDSigReferenceCtxPtr r;
  xmlSecBufferPtr buf;
  
  ret = malloc(sizeof(*ret));
  memset(ret, 0, sizeof(*ret));
  ret->unsignedRoot = root;
  
  fprintf(stderr, "a1\n");
  ret->sig = xmlSecFindChild(root, xmlSecNodeSignature, xmlSecDSigNs);
  assert(ret->sig);
  keyinfo = xmlSecFindChild(ret->sig, xmlSecNodeKeyInfo, xmlSecDSigNs);
  assert(keyinfo);
  ret->authorities = xmlSecFindChild(keyinfo, "authorities", "urn:certificate");

  fprintf(stderr, "a2\n");
  ret->signingKey = xmlSecKeyCreate();
  assert(ret->signingKey);
  extractKey(ret->signingKey, keyinfo);

  fprintf(stderr, "a3\n");
  ret->signingCtx = xmlSecDSigCtxCreate(NULL);
  fprintf(stderr, "a31\n");
  ret->signingCtx->signKey = ret->signingKey;
  fprintf(stderr, "a32\n");
  ret->signingCtx->flags |= XMLSEC_DSIG_FLAGS_STORE_SIGNEDINFO_REFERENCES;
  fprintf(stderr, "a33\n");
    printReferences(ret->signingCtx);
  if(xmlSecDSigCtxVerify(ret->signingCtx, ret->sig) || (ret->signingCtx->status != xmlSecDSigStatusSucceeded)) {
    fprintf(stderr, "signature validation failed\n");
    printReferences(ret->signingCtx);
    exit(1);
  }

  fprintf(stderr, "a4\n");
  for(i = 0; i < xmlSecPtrListGetSize(&ret->signingCtx->signedInfoReferences); i++) {
    r = xmlSecPtrListGetItem(&ret->signingCtx->signedInfoReferences, i);
    assert(r);
    buf = xmlSecDSigReferenceCtxGetPreDigestBuffer(r);
    assert(buf);
    fprintf(stderr, "signedinfo reference %d (%d):\n", i, buf->size);
    write(2, buf->data, buf->size);
    fprintf(stderr, "\n end signedinfo reference\n");
  }

  fprintf(stderr, "b\n");
  assert(xmlSecPtrListGetSize(&ret->signingCtx->signedInfoReferences) == 1);
  fprintf(stderr, "c\n");
  ref = (xmlSecDSigReferenceCtxPtr)xmlSecPtrListGetItem(&ret->signingCtx->signedInfoReferences, 0);
  fprintf(stderr, "d\n");
  assert(ref);
  fprintf(stderr, "e\n");
  content = xmlSecDSigReferenceCtxGetPreDigestBuffer(ref);
  fprintf(stderr, "f\n");
  assert(content);
  fprintf(stderr, "validateDocument:content is %s\n", xmlSecBufferGetData(content));
  
  parser = xmlSchemaNewParserCtxt(schema);
  assert(parser);
  schemap = xmlSchemaParse(parser);
  assert(schemap);
  vctx = xmlSchemaNewValidCtxt(schemap);
  assert(vctx);

  /* validate document */
  signedDoc = xmlParseMemory(xmlSecBufferGetData(content),
		  xmlSecBufferGetSize(content));
  assert(signedDoc);
  assert(!xmlSchemaValidateDoc(vctx, signedDoc));
  ret->signedRoot = xmlDocGetRootElement(signedDoc);

  return ret;
}

void resolveCertificates(struct signedDocument *sdoc, struct trustState *ts) {
  xmlNodePtr cur;
  
  if(!sdoc->authorities)
    return;
  /* resolve certificates */
  cur = sdoc->authorities->xmlChildrenNode;
  if(cur) 
    do {
      if(xmlSecCheckNodeName(cur, "authority", "urn:certificate")) {
	struct signedDocument *doc;
	xmlNodePtr ptr;

	doc = validateDocument("file://" DATADIR "/authority.xsd", cur);
	assert(doc);
	fprintf(stderr, "%s/%s\n", xmlSecNodeGetName(doc->signedRoot),
		xmlSecGetNodeNsHref(doc->signedRoot));
	assert(!xmlStrcmp(xmlSecNodeGetName(doc->signedRoot), "authority") &&
	       !xmlStrcmp(xmlSecGetNodeNsHref(doc->signedRoot), "urn:certificate"));
	ptr = doc->signedRoot->xmlChildrenNode;
	if(ptr)
	  do {
	    if(xmlSecCheckNodeName(ptr, xmlSecNodeKeyInfo, xmlSecDSigNs)) {
	      xmlSecKeyPtr signedKey;

	      signedKey = xmlSecKeyCreate();
	      assert(signedKey);
	      extractKey(signedKey, ptr);
#if 0
	      fprintf(stderr, "Have signed key:\n");
	      xmlSecKeyDebugDump(signedKey, stdout);
	      fprintf(stderr, "Signed by:\n");
	      xmlSecKeyDebugDump(doc->signingKey, stdout);
#endif
	      assertSigned(ts, signedKey, doc->signingKey);
#if 0
	      d = xmlSecOpenSSLKeyDataDsaGetDsa(xmlSecKeyGetValue(signedKey));
	      if(d && !BN_cmp(d->p, sk->p) &&
		 !BN_cmp(d->q, sk->q) &&
		 !BN_cmp(d->g, sk->g) &&
		 !BN_cmp(d->pub_key, sk->pub_key))
		fprintf(stderr, "Key %p matches signingKey %p!\n", d, sk);
	      else
		fprintf(stderr, "Key %p does not match signingKey %p.\n", d, sk);
#endif
	    } else
	      fprintf(stderr, "Not KI, %s/%s\n", xmlSecNodeGetName(ptr), xmlSecGetNodeNsHref(ptr));
	  } while((ptr = ptr->next));
	else
	  fprintf(stderr, "No KeyInfos in certificate??");
      } else {
	fprintf(stderr, "%s/%s\n", xmlSecNodeGetName(cur), xmlSecGetNodeNsHref(cur));
      }
    } while((cur = cur->next));
  else
    fprintf(stderr, "No authorities found\n");
}


xmlSecKeyPtr loadRootKey() {
  xmlSecKeyPtr key;
  key = xmlSecCryptoAppKeyLoad(SYSCONFDIR "/key.rsa", xmlSecKeyDataFormatPem, NULL, NULL, NULL);
  assert(key);
  return key;
}
